package com.learn.service.impl;

import com.learn.entity.OperationLog;
import com.learn.service.IOperationLogAsynTaskService;
import com.learn.service.IOperationLogService;
import com.learn.utils.NamedThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Slf4j
@Service
public class OperationLogAsynTaskService implements IOperationLogAsynTaskService {
    @Autowired
    private IOperationLogService operationLogService;

    private BlockingQueue<OperationLog> logQueue = new LinkedBlockingQueue<>();
    private ExecutorService executorService ;

    @Override
    public void add(OperationLog operationLog){
        if(operationLog!=null){
            logQueue.add(operationLog);
        }
    }

    @Override
    public OperationLog poll(){
        return logQueue.poll();
    }

    @Override
    public void init(){
        int processors = Runtime.getRuntime().availableProcessors();
        int corePoolSize = 1;
        if(processors<=2){
            corePoolSize=1;
        }else{
            corePoolSize=processors/2;
        }
        int maxPoolSize = processors*2;
        executorService = new ThreadPoolExecutor(corePoolSize,maxPoolSize,10L,
                TimeUnit.SECONDS,new LinkedBlockingQueue<>(),new NamedThreadFactory("AsynOperateLogExecutor"));
        for(int i=0;i<corePoolSize;i++){
            executorService.submit(new LogTask());
        }
    }

    class LogTask implements Runnable{

        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            int max = 500;
            List<OperationLog> operationLogs = new ArrayList<>();
            while(true){
                OperationLog operationLog = null;
                try {
                    operationLog = logQueue.poll(1000, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    log.error(Thread.currentThread().getName(),e);
                }
                if (operationLog!=null){
                    operationLogs.add(operationLog);
                    if(operationLogs.size()>=max){
                        operationLogService.saveBatch(operationLogs);
                        operationLogs.clear();
                    }
                }else{
                    operationLogService.saveBatch(operationLogs);
                    operationLogs.clear();
                }
            }
        }
    }
}
